﻿using System;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography;

namespace TK
{
    sealed class PropInfo
    {
        internal readonly string Name;
        internal readonly string StoreName;
        internal readonly Type Type;
        internal Func<object, object> GetValue;
        internal Action<object, object> SetValue;
        internal readonly Type CustomType;
        internal readonly bool IsProperty;
        internal readonly int Version;
        internal MemberInfo MemberInfo;
        internal readonly StoreAs storeAs;
        FieldInfo backField;

        internal PropInfo(PropertyInfo from)
        {
            Name = from.Name;
            Type = from.PropertyType;
            StoreName = getMemberName(from);
            GetValue = o => from.GetValue(o, null);
            SetValue = (obj, value) => from.SetValue(obj, value, null); // parse invocation exception ?
            IsProperty = true;
            MemberInfo = from;

            var attributes = from.GetCustomAttributes(true);
            var attrType = attributes.OfType<CSType>().FirstOrDefault();
            CustomType = attrType == null ? Type : attrType.Type;

            var attrVersion = attributes.OfType<CSVersion>().FirstOrDefault();
            Version = attrVersion == null ? 0 : attrVersion.Version;

            // CSDirect attribute only for properties
            var attrDirect = attributes.OfType<CSDirect>().FirstOrDefault();
            if (attrDirect != null && (attrDirect.Get || attrDirect.Set)) {
                backField = Type.GetField("<" + Name + ">k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic);
                if (backField != null) {
                    if (attrDirect.Get) GetValue = o => backField.GetValue(o);
                    if (attrDirect.Set) SetValue = (obj, value) => backField.SetValue(obj, value);
                }
            }

            if (Type.IsEnum)
            {
                var attrStoreAs = attributes.OfType<CSOption>().FirstOrDefault();
                if (attrStoreAs != null) storeAs = attrStoreAs.As;
            }
        }

        internal PropInfo(FieldInfo from)
        {
            Name = from.Name;
            Type = from.FieldType;
            StoreName = getMemberName(from);
            GetValue = from.GetValue;
            SetValue = from.SetValue;
            MemberInfo = from;

            var attributes = from.GetCustomAttributes(true);
            var attrType = attributes.OfType<CSType>().FirstOrDefault();
            CustomType = attrType == null ? Type : attrType.Type;

            var attrVersion = attributes.OfType<CSVersion>().FirstOrDefault();
            Version = attrVersion == null ? 0 : attrVersion.Version;

            if (Type.IsEnum)
            {
                var attrStoreAs = attributes.OfType<CSOption>().FirstOrDefault();
                if (attrStoreAs != null) storeAs = attrStoreAs.As;
            }
        }

        string getMemberName(MemberInfo memberInfo)
        {
            var name = memberInfo.GetCustomAttributes(typeof(CSName), true).OfType<CSName>().FirstOrDefault();
            return name == null ? memberInfo.Name : name.Name;
        }

#if DEBUG
        public override string ToString() { return Name + " => " + StoreName + ", Type=" + Type.Name; }
#endif
    }
}